/**
* @file task.c
* @author Jan Sliacky
* 
* Tasks processing unit
*
* April 2009
*
* Copyright (c) 2009 by Jan Sliacky
* All rights reserved.
*/

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "tasks.h"
#include "globals.h"

struct TaskContainer {		
								TASK task;
							   void* next;
							};

typedef struct TaskContainer TASKCONTAINER;

TASKCONTAINER* todoCont;						/* record of tasks to do				*/
TASKCONTAINER* solvedCont;						/* record of tasks to do				*/
TASK allTasks[MAXTASKCNT];	      				/* array of all tasks					*/
int nTasks;										/* count of all tasks					*/
int nSolvedTasks;								/* count of all solved tasks			*/
int nTasksToDo;									/* count of task to do 					*/
int nRecentlySolvedTasks;						/* count of recently solved tasks   */

/**
 * malloc pointer and exit on error
 * @author Jan Sliacky
 */
void *malloc_p(size_t size)
{
	void *p;

	if ((p = malloc(size)) == NULL) {
		printf("Alocation error\n");
		destroyTasksVariables();
		exit(1);
	}
	else {
		return(p);
	}
}

/**
 * Initialize tasks unit
 * @author Jan Sliacky
 */
int initTasks(void)
{
	nTasks = 0;
	nSolvedTasks = 0;
	initTasksVariables();
	return(EXIT_SUCCESS);
}

/**
 * Destroy tasks unit
 * @author Jan Sliacky
 */
void destroyTasks(void)
{
	destroyTasksVariables();
}

/**
 * Initialize tasks variables
 * @author Jan Sliacky
 */
void initTasksVariables(void)
{
	nTasksToDo = 0;
	nRecentlySolvedTasks = 0;
	todoCont = (TASKCONTAINER *) malloc_p(sizeof(TASKCONTAINER));
	todoCont->next = NULL;
	solvedCont = (TASKCONTAINER *) malloc_p(sizeof(TASKCONTAINER));
	solvedCont->next = NULL;
}

/**
 * Reset tasks variables
 * @author Jan Sliacky
 */
void resetTasksVariables(void)
{
	destroyTasksVariables();
	initTasksVariables();
}

/**
 * Destroy todoCont and solvedCont containers
 * @author Jan Sliacky
 */
void destroyTasksVariables(void)
{
	free(todoCont);
	free(solvedCont);
}

/**
 * Take first task from the TaskToDo container task that isn't solved
 * @author		Jan Sliacky
 * @param	t	taken task
 * @return		EXIT_SUCCESS on success, EXIT_SUCCESS on failure
 */
int takeFromTaskToDo(TASK* t)
{
	TASKCONTAINER* TCpom;
	
	do {
		if (nTasksToDo > 0) {
			*t = todoCont->task;
			TCpom = todoCont;
			todoCont = todoCont->next;
			free(TCpom);
			nTasksToDo -= 1;
		}
		else {
			t->ID = -1;
			return(EXIT_FAILURE);
		}
	}
	while (allTasks[t->ID].status == TASK_SOLVED);
	return(EXIT_SUCCESS);
}

/**
 * Add task to the TaskToDo container or update status of the task
 * @author Jan Sliacky
 * @param	t	task to add
 */
void addToTaskToDo(TASK* t)
{
	TASKCONTAINER* TCpom;

	t->value = allTasks[t->ID].value;
	if (t->status != TASK_UNSOLVED) {
		markTaskStatus(t, t->status);
	}
	else {
		if (allTasks[t->ID].status != TASK_SOLVED) {
			TCpom = (TASKCONTAINER *) malloc_p(sizeof(TASKCONTAINER));
			TCpom->task = *t;
			TCpom->next = todoCont;
			todoCont = TCpom;
			nTasksToDo += 1;
		}
	}
}

/**
 * Take task from the Solved container
 * @author Jan Sliacky
 * @param t	taken task
 * @return EXIT_SUCCESS on success, EXIT_FAILURE on failure
 */
int takeFromSolvedTasks(TASK* t)
{
	TASKCONTAINER* TCpom;

	if (nRecentlySolvedTasks > 0) {
		*t = solvedCont->task;
		/* for debug purposes */
		if (t->ID > nTasks || t->ID < 0 || t->status > 2 || t->status < 0) {
			printf("ERROR (takeFromSolvedTasks) task ID = %d, status = %d, value = %f taskov = %d\n\n", t->ID, t->status, t->value, nTasksToDo);fflush(stdout);
		}
		TCpom = solvedCont;
		solvedCont = solvedCont->next;
		free(TCpom);
		nRecentlySolvedTasks -= 1;
		return(EXIT_SUCCESS);
	}
	else {
		return(EXIT_FAILURE);
	}
}

/**
 * Add task to the Solved container
 * @author Jan Sliacky
 * @param t	task to add
 */
void addToSolvedTasks(TASK* t)
{
	TASKCONTAINER* TCpom;

	markTaskStatus(t, TASK_SOLVED);
	t->status = TASK_SOLVED;
	TCpom = (TASKCONTAINER *) malloc_p(sizeof(TASKCONTAINER));
	TCpom->task = *t;
	TCpom->next = solvedCont;
	solvedCont = TCpom;
	/* for debug purposes */
	if (t->ID > nTasks || t->ID < 0 || t->status > 2 || t->status < 0) {
		printf("ERROR (addToSolvedTasks) task ID = %d, status = %d, value = %f taskov = %d\n\n", t->ID, t->status, t->value, nTasksToDo);fflush(stdout);
	}
	nRecentlySolvedTasks += 1;
}

/**
 * Get count of the processors unresolved tasks
 * @author Jan Sliacky
 * @return count of the tasks to do
 */
int getToDoTaskCount(void)
{
	TASKCONTAINER* TCpom;
	TASK t;
	int i, ipom = 0;

	for (i = 0; i < nTasksToDo; i++) {
		if (i == 0) {
			TCpom = todoCont;
		}
		else {
			TCpom = TCpom->next;
		}
		t = TCpom->task;
		if (allTasks[t.ID].status != TASK_SOLVED) {
			ipom += 1;
		}
	}
	return(ipom);
}

/** 
 * Add task to task array
 * @author Jan Sliacky
 * @param task	inserted task
 */
void addTask(TASK* t)
{
	allTasks[nTasks] = *t;
	nTasks += 1;
}

/**
* Mark task status of given task
* @author Jan Sliacky
* @param	t		task to mark
* @param	ts		task status
*/
void markTaskStatus(TASK* t, enum TaskStatuses ts)
{
	/* i still haven't a task marked as solved */
	if (allTasks[t->ID].status != TASK_SOLVED) {
		allTasks[t->ID].status = ts;
		if (ts == TASK_SOLVED) {
			/* if task status is not TASK_ASSIGN, increment solved tasks counter */
			nSolvedTasks += 1;
		}
	}
}

/** 
 * Mark all task statuses as unsolved
 * @author Jan Sliacky
 */
void clearTasksStatuses(void)
{
	int i;
	
	for(i = 0; i < nTasks; i++) {
		allTasks[i].status = TASK_UNSOLVED;
	}
	nSolvedTasks = 0;
}

/** 
 * Are all tasks solved ?
 * @author	Jan Sliacky
 * @return	true when all tasks are solved or false when not
 */
_Bool areAllSolved(void)
{
	if (nTasks == nSolvedTasks) {
		return(true);
	}
	else {
		return(false);
	}
}

/**
 * Is any unassign task ?
 * @author	Jan Sliacky
 * @return	true if is unassign task, false if not
 */
_Bool isUnassignTask(void)
{
	int i;
	_Bool ipom = false;

	for(i = 0; i < nTasks; i++) {
		if (allTasks[i].status == TASK_UNSOLVED) {
			ipom = true;
		}
	}
	return ipom;
}

/**
 * Return next solved task ID from index "from"
 * @author Jan Sliacky
 * @param	*t		returned solved task
 * @param	from	index which from search solved task
 * @return EXIT_SUCCESS on success, EXIT_FAILURE when failed
 */
int getNextSolvedTask(TASK *t, int from)
{
	int i;
	for (i = from; i < nTasks; i++)	{
		if (allTasks[i].status == TASK_SOLVED) {
			*t = allTasks[i];
			return(EXIT_SUCCESS);
		}
	}
	return(EXIT_FAILURE);
}

/**
 * Get all unassign tasks
 * @author	Jan Sliacky
 * @return	amount of assign tasks
 */
int getUnassignTasks(void)
{
	int i;
	int from_index;

	srand(time(NULL));
	from_index = (int)((((double) rand())/((double) RAND_MAX))*MAXTASKCNT);
	for(i = from_index; i < (nTasks + from_index); i++) {
		if (allTasks[i%nTasks].status == TASK_UNSOLVED) {
			addToTaskToDo(&allTasks[i%nTasks]);
		}
	}
	return(nTasksToDo);
}

/**
 * Get random assign task
 * @author	Jan Sliacky
 * @return	true on success assign
 */
int getFirstAssignTask(void)
{
	int i;
	TASK t;

	for(i = 0; i < nTasks; i++) {
		if (allTasks[i].status == TASK_ASSIGN) {
			t = allTasks[i];
			t.status = TASK_UNSOLVED;
			addToTaskToDo(&t);
			return(EXIT_SUCCESS);
		}
	}
	return(EXIT_FAILURE);
}

/**
 * Change assign tasks statuses to unsolved
 * @author	Jan Sliacky
 */
void clearAssignedTasks(void)
{
	int i;

	for(i = 0; i < nTasks; i++) {
		if (allTasks[i].status == TASK_ASSIGN) {
			allTasks[i].status = TASK_UNSOLVED;
		}
	}
}

/**
 * Get count of all tasks
 * @author	Jan Sliacky
 * @return	count of tasks
 */
int getTasksCount(void)
{
	return(nTasks);
}

/**
 * Get count of all solved tasks
 * @author	Jan Sliacky
 * @return	count of solved tasks
 */
int getSolvedTasksCount(void)
{
	return(nSolvedTasks);
}

/**
 * Get status of the task
 * @author	Jan Sliacky
 * @param	taskID	ID of the task
 * @return	status of the task
 */
int getTaskStatus(int taskID)
{
	return(allTasks[taskID].status);
}	

/**
 * Get count of recently solved tasks
 * @author	Jan Sliacky
 * @return	count of recently solved tasks
 */
int getRecentlySolvedTasksCount(void)
{
	return(nRecentlySolvedTasks);
}
